Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | import {
ActionIcon,
Box,
Checkbox,
Group,
NumberInput,
Paper,
Stack,
Text,
Title,
} from '@mantine/core';
import { IconMinus, IconPlus } from '@tabler/icons-react';
import { useTranslations } from 'next-intl';
import classes from '../RecipeDetail.module.css';
import type { RecipeIngredientsProps } from '../types';
import { scaleQuantity } from '../utils';
const SERVING_MIN = 1;
const SERVING_MAX = 20;
export const RecipeIngredients = ({
ingredients,
servingMultiplier,
adjustedServings,
checkedIngredients,
onToggleIngredient,
onIncrementServings,
onDecrementServings,
}: Readonly<RecipeIngredientsProps>) => {
const translate = useTranslations('recipeDetail');
const ingTranslate = useTranslations('recipeIngredients');
return (
<Paper p="lg" radius="md" withBorder className={classes.ingredientsCard}>
<Group justify="space-between" mb="md">
<Title order={2} size="h3" c="pink">
{translate('ingredients')}
</Title>
<Text size="xs" c="dimmed">
{translate('checkedOff', {
count: checkedIngredients.size,
total: ingredients.length,
})}
</Text>
</Group>
{/* Serving adjuster */}
<Group gap="xs" mb="lg" justify="center">
<Text fw={700} size="sm" tt="uppercase">
{translate('servings')}:
</Text>
<ActionIcon
variant="filled"
color="pink"
size="sm"
onClick={onDecrementServings}
disabled={servingMultiplier <= SERVING_MIN}
aria-label={ingTranslate('decreaseServings')}
>
<IconMinus size={14} />
</ActionIcon>
<NumberInput
value={adjustedServings}
readOnly
hideControls
w={50}
size="xs"
styles={{ input: { textAlign: 'center', fontWeight: 700 } }}
/>
<ActionIcon
variant="filled"
color="pink"
size="sm"
onClick={onIncrementServings}
disabled={servingMultiplier >= SERVING_MAX}
aria-label={ingTranslate('increaseServings')}
>
<IconPlus size={14} />
</ActionIcon>
</Group>
{/* Ingredient list */}
<Stack gap={0}>
{ingredients.map((ing) => {
const checked = checkedIngredients.has(ing.localId);
const scaledQty = scaleQuantity(ing.quantity, servingMultiplier);
return (
<Box
key={ing.localId}
className={`${classes.ingredientItem} ${checked ? classes.ingredientChecked : ''}`}
onClick={() => onToggleIngredient(ing.localId)}
>
<Checkbox
checked={checked}
onChange={() => onToggleIngredient(ing.localId)}
color="pink"
size="sm"
tabIndex={-1}
aria-label={ing.name}
/>
<Text
size="sm"
className={`${classes.ingredientText} ${checked ? classes.ingredientTextChecked : ''}`}
>
<Text component="span" fw={700}>
{scaledQty} {ing.unit}
</Text>{' '}
{ing.name}
</Text>
</Box>
);
})}
</Stack>
</Paper>
);
};
|